/**
 * 
 */
package whf.framework.security;

import java.security.Principal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import whf.framework.i18n.ApplicationResource;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.security.entity.Dept;
import whf.framework.security.entity.Group;
import whf.framework.security.entity.Permission;
import whf.framework.security.entity.Role;
import whf.framework.security.entity.User;
import whf.framework.security.service.DeptService;
import whf.framework.security.service.DeptServiceImp;
import whf.framework.security.service.RoleServiceImp;
import whf.framework.security.service.UserServiceImp;
import whf.framework.util.BeanFactory;
import whf.framework.util.Pair;
import whf.framework.util.StringUtils;
import whf.framework.util.Utils;
import whf.framework.web.WebContextHolder;
import whf.framework.web.query.Field;
import whf.framework.web.tag.ext.TableAttributes;

/**
 * @author wanghaifeng
 * @create Aug 13, 2006 12:14:14 AM
 * 具体只需要与登陆逻辑关联即可
 */
public class UserContextImp implements UserContext {
	private static Log log = LogFactory.getLog(UserContextImp.class);
	
	private Set<Dept> managedDepts;
	
	private String sessionId;
	private String remoteIpAddress;
	private User user;
	private Locale locale; 
	private long loginTime;
	private Map<String, Object> sessionMap = Utils.newHashMap();
	private Principal principal;
	private Pair<Integer, Integer> clientSize = new Pair<Integer, Integer>(1024, 768);
	
	/**
	 * 用户是否是超级管理员
	 * @property boolean:isAdministrator
	 */
	private boolean isAdministrator;
	
	/**
	 * 用户管理器
	 * @property UserManager:userManager
	 */
	private UserManager userManager;
	
	/**
	 * 用户所处的角色，非匿名角色
	 * @property Set<Role>:roles
	 */
	private Set<Role> roles;
	
	/**
	 * 系统匿名角色
	 * @property Set<Role>:anonymousRoles
	 */
	private Set<Role> anonymousRoles;
	
	/**
	 * 用户所拥有的所有权限，包括匿名用户权限和登陆用户的权限
	 * @property Set<Permission>:permissions
	 */
	private Set<Permission> permissions;
	
	/**
	 * 用户所属的组
	 * @property Set<Group>:groups
	 */
	private Set<Group> groups;
	
	/**
	 * 缓存列对象
	 * @property Map<String,List<TableColumn>>:tableColumns
	 */
	private Map<String, TableAttributes> tablesAttributes = Utils.newHashMap();
	
	private Map<String, List<Field>> queryFieldValues = Utils.newHashMap();
	
	/**
	 * @modify wanghaifeng Aug 13, 2006 12:57:53 AM
	 * @param request
	 * @return 匿名用户上下文
	 */
	public static UserContext createAnonymousUserContext(HttpServletRequest request) throws UserContextException{
		UserContext uc = new UserContextImp(request);
		return uc;
	}
	
	/**
	 * @modify wanghaifeng Aug 15, 2006 12:02:20 AM
	 * @param session
	 * @return
	 * @throws UserContextException
	 */
	public static UserContext createAnonymousUserContext(HttpSession session) throws UserContextException{
		UserContext uc = new UserContextImp(session);
		return uc;
	}
	/**
	 * @modify wanghaifeng Aug 13, 2006 11:35:26 PM
	 * 适用于在jsp或者servlet中自己实现登陆的情况
	 * @param user
	 * @param request
	 * @return
	 * @throws UserContextException
	 */
	public static UserContext createUserContext(User user, Set<Role> roles, HttpServletRequest request) throws UserContextException{
		return new UserContextImp(user, roles, request);
	}

	/**
	 * @modify wanghaifeng Aug 14, 2006 11:36:11 PM
	 * 从Session中初始化相关信息；
	 * @param user
	 * @param roles
	 * @param session
	 * @return
	 * @throws UserContextException
	 */
	public static UserContext createUserContext(User user, Set<Role> roles, HttpSession session) throws UserContextException{
		return new UserContextImp(user, roles, session);
	}
	
	/**
	 * @param request
	 * @throws UserContextException
	 */
	private UserContextImp(HttpServletRequest request) throws UserContextException{
		this.user = new User();
		this.initUserContext(request);
	}
	
	private UserContextImp(HttpSession session) throws UserContextException{
		this.user = new User();
		this.initUserContext(session);
	}
	
	/**
	 * @param user 登陆成功的用户对象
	 * @param request 当前请求对象
	 * @throws UserContextException 初始化用户上下文失败
	 */
	private UserContextImp(User user, Set<Role> roles, HttpServletRequest request) throws UserContextException{
		if(user == null) throw new UserContextException(ApplicationResource.get("user_context.user_empty"));
		this.user = user;
		this.roles = roles;
		this.initUserContext(request);
	}
	
	/**
	 * 从Session中初始化相关信息；
	 * @param user
	 * @param roles
	 * @param session
	 * @throws UserContextException
	 */
	private UserContextImp(User user, Set<Role> roles, HttpSession session) throws UserContextException{
		if(user == null) throw new UserContextException(ApplicationResource.get("user_context.user_empty"));
		this.user = user;
		this.roles = roles;
		this.initUserContext(session);
	}
	
	/**
	 * @modify wanghaifeng Aug 13, 2006 2:42:41 PM
	 * 初始化用户上下文
	 * @param request
	 */
	private void initUserContext(HttpServletRequest request) throws UserContextException {
		this.remoteIpAddress = request.getRemoteAddr();
		this.locale = request.getLocale();
		this.principal = request.getUserPrincipal();
		this.initUserContext(request.getSession());
	}
	
	private void initUserContext(HttpSession session) throws UserContextException{
		this.sessionId = session.getId();
		this.loginTime = System.currentTimeMillis();
		session.setAttribute(UserContext.USER_CONTEXT, this);
		this.initUserContext(null, null);
	}
	
	/**
	 * @modify wanghaifeng Aug 13, 2006 2:45:04 PM
	 * @throws UserContextException
	 */
	private void initUserContext(User user, Set<Role> roles) throws UserContextException{
		try{
			//初始化匿名用户权限（角色）
			if(this.permissions == null){
				this.permissions = Utils.newHashSet();
			} else {
				this.permissions.clear();
			}
			this.anonymousRoles = RoleServiceImp.getRoleService().findAnonymousRoles();
			for(Role r: this.anonymousRoles){
				if(StringUtils.equalsIgnoreCase("administrator",r.getName())){
					this.isAdministrator = true;
				}
				this.permissions.addAll(r.getPermissions());
			}
			this.roles = roles;
			if(this.roles != null){
				for(Role r: this.roles){
					if(StringUtils.equalsIgnoreCase("administrator",r.getName())){
						this.isAdministrator = true;
					}
					this.permissions.addAll(r.getPermissions());
				}
			}
			//
			this.user = user;
			if(this.user != null){
				if(this.userManager == null){
					userManager = (UserManager)BeanFactory.getBean("framework.UserManager");
				}
				this.groups = userManager.findGroups(this.user);
			}
		}catch(Exception e){
			throw new UserContextException(e);
		}
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#upgradeToUser(whf.framework.auth.entity.User, java.util.Set)
	 */
	public void upgradeToUser(Principal principal, User user, Set<Role> roles) throws UserContextException{
		this.permCache.clear();
		user.setLastLogin(new java.sql.Timestamp(System.currentTimeMillis()));
		user.setLastLoginIP(this.getRemoteIpAddress());
		try{
			UserServiceImp.getUserService().update(user);
		}catch(Exception e){
			log.warn(this, e);
		}
		if(roles == null){
			roles = HibernateSpringUserManager.getUserManager().findRoles(user);
		}
		this.initUserContext(user, roles);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getUser()
	 */
	public User getUser() {
		return this.user;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getLocale()
	 */
	public Locale getLocale() {
		return this.locale;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getRoles()
	 */
	public Set<Role> getRoles() {
		return this.roles;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getGroups()
	 */
	public Set<Group> getGroups() {
		return this.groups;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getPermissions()
	 */
	public Set<Permission> getPermissions() {
		return this.permissions;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getSessionId()
	 */
	public String getSessionId() {
		return this.sessionId;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getTableAttributes(java.lang.String)
	 */
	public TableAttributes getTableAttributes(String tableId) {
		return this.tablesAttributes.get(tableId);
	}

	public void setTableAttributes(String tableId, TableAttributes attribute) {
		attribute.setTableId(tableId);
		attribute.setUser(this.user);
		this.tablesAttributes.put(tableId, attribute);
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#cleanTableColumns()
	 */
	public void cleanTablesAttributes() {
		this.tablesAttributes.clear();
	}

	public void setQueryFieldValues(String queryId, List<Field> fields) {
		this.queryFieldValues.put(queryId, fields);
	}

	public List<Field> getQueryFieldValues(String queryId) {
		return this.queryFieldValues.get(queryId);
	}

	public void cleanQueryFieldValues() {
		this.queryFieldValues.clear();
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getLoginTime()
	 */
	public long getLoginTime() {
		return this.loginTime;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getRemoteIpAddress()
	 */
	public String getRemoteIpAddress() {
		WebContextHolder wc = WebContextHolder.getWebContext();
		if(StringUtils.isEmpty(this.remoteIpAddress) && wc != null && wc.getRequest() != null){
			this.remoteIpAddress = wc.getRequest().getRemoteAddr();
		}
		return remoteIpAddress;
	}

	/**
	 * @return Returns the isAdministrator.
	 */
	public final boolean isAdministrator() {
		return isAdministrator;
	}
	
	//
	final Map<String, Boolean> permCache = Utils.newHashMap();
	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#hasFunctionPermission(java.lang.String, java.lang.String)
	 */
	final public boolean hasFunctionPermission(String targetType, String func) {
		if (this.isAdministrator) {
			return true;
		} else {
			if (this.permCache.get("*@" + targetType) != null)
				return true;

			Boolean b1 = this.permCache.get(func + "@" + targetType);
			if (b1 != null) {
				return b1.booleanValue();
			} else {
				boolean result = false;
				for (Permission p : this.permissions) {
					if (!StringUtils.equals(p.getTarget(), targetType))
						continue;
					if (StringUtils.equals("*", p.getOperation()) || StringUtils.equals("*", p.getPrepareOperation())) {
						result = true;
						this.permCache.put("*@" + targetType, new Boolean(result));
						break;
					} else if (StringUtils.equals(func, p.getOperation()) || StringUtils.equals(func, p.getPrepareOperation())) {
						result = true;
						this.permCache.put(p.getOperation() + "@" + targetType, new Boolean(result));
						this.permCache.put(p.getPrepareOperation() + "@" + targetType, new Boolean(result));
						break;
					} else if(StringUtils.isEmpty(p.getOperation()) && StringUtils.equals(func, String.valueOf(p.getId()))) {
						result = true;
						this.permCache.put(func + "@" + targetType, new Boolean(result));
						break;
					}
				}
				if(!result) {
					this.permCache.put(func + "@" + targetType, new Boolean(result));
					this.permCache.put(func + "@" + targetType, new Boolean(result));
				}
				return result;
			}
		}
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#hasPermission(whf.framework.auth.entity.Permission)
	 */
	public boolean hasPermission(Permission perm) {
		if(perm == null) return false;
		String op = perm.getOperation();
		if(StringUtils.isEmpty(op)) op = String.valueOf(perm.getId());
		return this.hasFunctionPermission(perm.getTarget(), op);
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getSessionObject(java.lang.String)
	 */
	public Object getSessionObject(String key) {
		return this.sessionMap.get(key);
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#setSessionObject(java.lang.String, java.lang.Object)
	 */
	public void setSessionObject(String key, Object val) {
		if(val == null) return;
		this.sessionMap.put(key, val);
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#removeSessionObject(java.lang.String)
	 */
	public void removeSessionObject(String key) {
		this.sessionMap.remove(key);
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getPrincipal()
	 */
	public Principal getPrincipal() {
		return principal;
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#getClientSize()
	 */
	public int[] getClientSize() {
		return new int[]{this.clientSize.getText().intValue(), this.clientSize.getValue().intValue()};
	}

	/* (non-Javadoc)
	 * @see whf.framework.auth.UserContext#setClientSize(int, int)
	 */
	public void setClientSize(int width, int height) {
		this.clientSize.setText(width);
		this.clientSize.setValue(height);
	}

	public Set<Dept> getManagedDepts() {
		if(this.managedDepts == null){
			this.managedDepts = Utils.newHashSet();
			boolean isManager = false;
			for(Role role: this.roles){
				if(role.isManagerRole()) {
					isManager = true;
					break;
				}
			}
			if(isManager){
				this.managedDepts.add(this.user.getDept());
				try{
					DeptService deptService = DeptServiceImp.getDeptService();
					this.initChildrenDepts(this.managedDepts, deptService, this.user.getDept());
				} catch(Exception e){
					log.error(this, e);
				}
			}
		}
		return managedDepts;
	}
	
	private void initChildrenDepts(Set<Dept> managedDepts, DeptService deptService, Dept dept) throws Exception{
		Set<Dept> children = Utils.newHashSet();
		children.addAll(deptService.findChildren(dept, null));
		managedDepts.addAll(children);
		for(Dept child: children){
			initChildrenDepts(managedDepts,  deptService, child);
		}
	}

	@Override
	public String getBranch() {
		String result = null;
		if(this.user != null) {
			result = this.user.getBranch();
		}
		if(StringUtils.isEmpty(result) && this.user!= null && this.user.getDept() != null) {
			result = this.user.getDept().getBranch();
		}
		return result;
	}

	@Override
	public boolean isMemberOfRole(String roleName) {
		for(Role r: this.roles) {
			if(StringUtils.equals(roleName, r.getName()) || StringUtils.equals(roleName, r.getCode())) {
				return true;
			}
		}
		for(Role r: this.anonymousRoles) {
			if(StringUtils.equals(roleName, r.getName()) || StringUtils.equals(roleName, r.getCode())) {
				return true;
			}
		}
		return false;
	}
	
	
}
